package br.com.infocruz.seam.crm.ejb.model;

import java.io.Serializable;
import java.util.List;
import java.util.regex.Pattern;

public class EntityList<T> implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 6208826100573978102L;

	private static final Pattern ORDER_COLUMN_PATTERN = Pattern.compile("^\\w+(\\.\\w+)*$");
	public static final String DIR_ASC = "asc";
	public static final String DIR_DESC = "desc";
	private static final String LOGIC_OPERATOR_AND = "and";
	private static final String LOGIC_OPERATOR_OR = "or";

	private T entityInstance;
	private List<T> resultList;
	private Integer resultCount = 0;
	private Integer firstResult = 0;
	private Integer maxResults = 25;
	private String order;
	private String orderColumn;
	private String orderDirection;
	private String restrictionLogicOperator;

	public EntityList() {
	}

	public EntityList(Integer firstResult, Integer maxResults) {
		super();
		this.firstResult = firstResult;
		this.maxResults = maxResults;
	}
	
	public EntityList(List<T> resultList, Integer resultCount) {
		super();
		this.resultList = resultList;
		this.resultCount = resultCount;
	}
	
	public EntityList(List<T> resultList, Integer resultCount, Integer firstResult, Integer maxResults) {
		super();
		this.resultList = resultList;
		this.resultCount = resultCount;
		this.firstResult = firstResult;
		this.maxResults = maxResults;
	}

	public EntityList(Integer firstResult, Integer maxResults, String orderDirection, String orderColumn) {
		super();
		this.firstResult = firstResult;
		this.maxResults = maxResults;
		this.orderDirection = sanitizeOrderDirection(orderDirection);
		this.orderColumn = sanitizeOrderColumn(orderColumn);
	}
	
	public T getEntityInstance() {
		return entityInstance;
	}

	public void setEntityInstance(T entityInstance) {
		this.entityInstance = entityInstance;
	}
	
	public List<T> getResultList() {
		return resultList;
	}

	public void setResultList(List<T> resultList) {
		this.resultList = resultList;
	}

	public Integer getResultCount() {
		return resultCount;
	}

	public void setResultCount(Integer resultCount) {
		this.resultCount = resultCount;
	}

	/**
	 * Returns the index of the first result of the current page
	 */
	public Integer getFirstResult() {
		return firstResult;
	}

	/**
	 * Returns true if the previous page exists
	 */
	public boolean isPreviousExists() {
		return getFirstResult() != null && getFirstResult() != 0;
	}

	/**
	 * Returns true if next page exists
	 */
	public boolean isNextExists() {
		return resultList != null && getMaxResults() != null && getResultCount() != null && getFirstResult() + getMaxResults() < getResultCount();
	}

	/**
	 * Returns true if the query is paginated, revealing whether navigation
	 * controls are needed.
	 */
	public boolean isPaginated() {
		return isNextExists() || isPreviousExists();
	}

	/**
	 * Set the index at which the page to display should start
	 */
	public void setFirstResult(Integer firstResult) {
		this.firstResult = firstResult;
	}

	/**
	 * The page size
	 */
	public Integer getMaxResults() {
		return maxResults;
	}

	public void setMaxResults(Integer maxResults) {
		this.maxResults = maxResults;
	}

	/**
	 * The order clause of the query
	 */

	public String getOrder() {
		String column = getOrderColumn();

		if (column == null) {
			return order;
		}

		String direction = getOrderDirection();

		if (direction == null) {
			return column;
		} else {
			return column + ' ' + direction;
		}
	}

	public void setOrder(String order) {
		this.order = order;
	}

	public String getOrderDirection() {
		return orderDirection;
	}

	public void setOrderDirection(String orderDirection) {
		this.orderDirection = sanitizeOrderDirection(orderDirection);
	}

	private String sanitizeOrderDirection(String direction) {
		if (direction == null || direction.length() == 0) {
			return null;
		} else if (direction.equalsIgnoreCase(DIR_ASC)) {
			return DIR_ASC;
		} else if (direction.equalsIgnoreCase(DIR_DESC)) {
			return DIR_DESC;
		} else {
			throw new IllegalArgumentException("invalid order direction");
		}
	}

	public String getOrderColumn() {
		return orderColumn;
	}

	public void setOrderColumn(String orderColumn) {
		this.orderColumn = sanitizeOrderColumn(orderColumn);
	}

	private String sanitizeOrderColumn(String columnName) {
		if (columnName == null || columnName.trim().length() == 0) {
			return null;
		} else if (ORDER_COLUMN_PATTERN.matcher(columnName).find()) {
			return columnName;
		} else {
			throw new IllegalArgumentException("invalid order column (\"" + columnName + "\" must match the regular expression \"" + ORDER_COLUMN_PATTERN + "\")");
		}
	}

	public String getRestrictionLogicOperator() {
		return restrictionLogicOperator != null ? restrictionLogicOperator : LOGIC_OPERATOR_AND;
	}

	public void setRestrictionLogicOperator(String operator) {
		restrictionLogicOperator = sanitizeRestrictionLogicOperator(operator);
	}

	private String sanitizeRestrictionLogicOperator(String operator) {
		if (operator == null || operator.trim().length() == 0) {
			return LOGIC_OPERATOR_AND;
		}
		if (!(LOGIC_OPERATOR_AND.equals(operator) || LOGIC_OPERATOR_OR.equals(operator))) {
			throw new IllegalArgumentException("Invalid restriction logic operator: " + operator);
		} else {
			return operator;
		}
	}
	
	/**
	 * Move the result set cursor to the beginning of the last page
	 * 
	 */
	public void last() {
		setFirstResult(getLastFirstResult().intValue());
	}

	/**
	 * Move the result set cursor to the beginning of the next page
	 * 
	 */
	public void next() {
		setFirstResult(getNextFirstResult());
	}

	/**
	 * Move the result set cursor to the beginning of the previous page
	 * 
	 */
	public void previous() {
		setFirstResult(getPreviousFirstResult());
	}

	/**
	 * Move the result set cursor to the beginning of the first page
	 * 
	 */
	public void first() {
		setFirstResult(0);
	}

	/**
	 * Get the index of the first result of the last page
	 * 
	 */
	public Long getLastFirstResult() {
		Integer pc = getPageCount();
		return pc == null ? null : (pc.longValue() - 1) * getMaxResults();
	}

	/**
	 * Get the index of the first result of the next page
	 * 
	 */
	public int getNextFirstResult() {
		Integer fr = getFirstResult();
		return (fr == null ? 0 : fr) + getMaxResults();
	}

	/**
	 * Get the index of the first result of the previous page
	 * 
	 */
	public int getPreviousFirstResult() {
		Integer fr = getFirstResult();
		Integer mr = getMaxResults();
		return mr >= (fr == null ? 0 : fr) ? 0 : fr - mr;
	}

	/**
	 * Get the total number of pages
	 * 
	 */
	public Integer getPageCount() {
		if (getMaxResults() == null) {
			return null;
		} else {
			int rc = getResultCount().intValue();
			int mr = getMaxResults().intValue();
			int pages = rc / mr;
			return rc % mr == 0 ? pages : pages + 1;
		}
	}

	public Integer getCurrentPage() {
		if (getMaxResults() == null) {
			return null;
		} else {
			int rc = getFirstResult().intValue();
			int mr = getMaxResults().intValue();
			if (rc > 0) {
				int pages = (rc / mr);
				return pages + 1;
				// return rc % mr == 0 ? pages : pages + 1;
			}
			return 1;
		}
	}

}
